package com.bill99.rpc.config.spring.schema;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.bill99.rpc.common.Constants;
import com.bill99.rpc.reference.InvokeMethod;
import com.bill99.rpc.reference.hessian.HessianReferenceBean;

/**
 * @author sheng.zhang
 */
public class ReferenceBeanDefinationParser extends
		AbstractRpcBeanDefinationParser {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Override
	protected Class<?> getBeanClass(Element element) {
		return HessianReferenceBean.class;
	}

	@Override
	protected String resolveId(Element element,
			AbstractBeanDefinition definition, ParserContext parserContext)
			throws BeanDefinitionStoreException {
		return element.getAttribute("name");
	}

	@Override
	protected void doParse(Element element, ParserContext parserContext,
			BeanDefinitionBuilder builder) {
		logger.info("to parse reference configuration");
		String interfaceName = element.getAttribute(Constants.INTERFACE_KEY);
		if (interfaceName != null && interfaceName.trim().length() > 0) {
			builder.addPropertyValue("serviceInterface", interfaceName);
		}
		String lbmethod = element.getAttribute(Constants.LBMETHOD_KEY);
		if (lbmethod != null && lbmethod.trim().length() > 0) {
			builder.addPropertyValue("lbmethod", lbmethod);
		}
		String failover = element.getAttribute(Constants.FAILOVER_KEY);
		if (failover != null && failover.trim().length() > 0) {
			if ("y".equals(failover.toLowerCase()))
				builder.addPropertyValue("failover", true);
			else if("n".equals(failover.toLowerCase()))
				builder.addPropertyValue("failover", false);
			else
				parserContext.getReaderContext().error(
						"Attribute[failover] must be y or n.", element);
		}
		NodeList nodeList = element.getChildNodes();
		parseMethods("", nodeList, builder.getBeanDefinition(), parserContext);
	}

	private void parseMethods(String id, NodeList nodeList,
			AbstractBeanDefinition beanDefinition, ParserContext parserContext) {
		if (nodeList != null && nodeList.getLength() > 0) {
			ManagedList methods = new ManagedList();
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				if (node instanceof Element) {
					Element element = (Element) node;
					if ("method".equals(node.getNodeName())
							|| "method".equals(node.getLocalName())) {
						String methodName = element.getAttribute("name");
						String failover = element.getAttribute("failover");
						RootBeanDefinition methodBeanDefinition = new RootBeanDefinition();
						methodBeanDefinition.setBeanClass(InvokeMethod.class);
						methodBeanDefinition.getPropertyValues()
								.addPropertyValue("name", methodName);
						if ("yes".equals(failover))
							methodBeanDefinition.getPropertyValues()
									.addPropertyValue("failover", true);
						else
							methodBeanDefinition.getPropertyValues()
									.addPropertyValue("failover", false);
						String name = id + "." + methodName;
						BeanDefinitionHolder methodBeanDefinitionHolder = new BeanDefinitionHolder(
								methodBeanDefinition, name);
						methods.add(methodBeanDefinitionHolder);
					}
				}
			}
			if (methods != null) {
				beanDefinition.getPropertyValues().addPropertyValue(
						"invokeMethods", methods);
			}
		}
	}
}
